package org.tlang.lexer;

import org.tlang.exception.ParseException;
import org.tlang.keywords.KeyWords;
import org.tlang.lexer.token.*;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Lexer {
    private static final String blankPat = "\\s*"; // 空白字符
    private static final String commentPat = "(//.*)"; // 单行注释，group 2
    private static final String floatPat = "([0-9]+\\.[0-9+])"; // float, example: 12.01
    private static final String integerPat = "([0-9]+)"; // NUMBER，例如 012，group 3
    // 字符串，等价于：("(\\"|\\\\|\\n|[^"])*")，例如 "hello \"tony\""，group 4
    private static final String stringPat = "(\"(\\\\\"|\\\\\\\\|\\\\n|[^\"])*\")";
    private static final String identifierPat = "[A-Z_a-z][A-Z_a-z0-9]*"; // 标识符，例如 abc_1
    private static final String binaryPunctuationPat = "==|!=|<=|>=|&&|\\|\\||--|\\+\\+"; // 组合符号
    private static final String singlePunctuationPat = "\\p{Punct}"; // 单个符号，例如：, . + -

    private static final String noneBlankPat = "" + // 非空字符串，group 1
            commentPat + "|" + // group 2
            floatPat + "|" + // group 3
            integerPat + "|" + // group 4
            stringPat + "|" + // group 5
            identifierPat + "|" +
            binaryPunctuationPat + "|" +
            singlePunctuationPat;

    private static final String regexPat = blankPat + "(" + noneBlankPat + ")?";

    private final Pattern pattern = Pattern.compile(regexPat);

    private final List<Token> tokens = new ArrayList<>();

    private void parseLine(String line, int lineNumber) throws ParseException {
        Matcher matcher = pattern.matcher(line);
        matcher.useTransparentBounds(true).useAnchoringBounds(false);
        int pos = 0;
        int endPos = line.length();
        while (pos < endPos) {
            matcher.region(pos, endPos);
            if (matcher.lookingAt()) {
                addToken(lineNumber, matcher);
                pos = matcher.end();
            } else {
                throw new ParseException("bad token at line = " + lineNumber);
            }
        }
//        tokens.add(new IdToken(lineNumber, Token.EOL));
    }

    private void addToken(int lineNumber, Matcher matcher) {
        String noneBlankPart = matcher.group(1); // 非空字符串部分匹配结果
        if (noneBlankPart == null) { // 空白
            return;
        }
        if (matcher.group(2) != null) { // 注释
            return;
        }

        Token token;
        if (matcher.group(3) != null) { // 浮点
            token = new F64Token(lineNumber, Double.parseDouble(noneBlankPart));
        } else if (matcher.group(4) != null) { // 数字
            token = new I64Token(lineNumber, Long.parseLong(noneBlankPart));
        } else if (matcher.group(5) != null) {
            token = new StrToken(lineNumber, toStringLiteral(noneBlankPart));
        } else {
            if (KeyWords.LITERAL_TRUE.equals(noneBlankPart) || KeyWords.LITERAL_FALSE.equals(noneBlankPart)) {
                token = new BoolToken(lineNumber, Boolean.parseBoolean(noneBlankPart));
            } else {
                token = new NameToken(lineNumber, noneBlankPart);
            }
        }
        tokens.add(token);
    }

    private static String toStringLiteral(String str) {
        StringBuilder stringBuilder = new StringBuilder();
        int len = str.length() - 1;
        for (int i = 1; i < len; i++) {
            char current = str.charAt(i);
            if (current == '\\' && i + 1 < len) { // 转义字符
                char currentNext = str.charAt(i + 1); // 当前字符的下一个字符串
                if (currentNext == '"' || currentNext == '\\') { // \" 或者 \\
                    current = str.charAt(++i); // 跳过转义符号，取下一个字符
                } else if (currentNext == 'n') { // 回车 \n
                    i++; // 跳过转义符号，有效字符转换为 \n
                    current = '\n';
                }
            }
            stringBuilder.append(current);
        }
        return stringBuilder.toString();
    }

    public void parse(String line) throws ParseException {
        parseLine(line, 0);
        tokens.add(Token.EOF);
    }

    public void parse(List<String> lines) throws ParseException {
        for (int i = 0; i < lines.size(); i++) {
            parseLine(lines.get(i), i + 1);
        }
        tokens.add(Token.EOF);
    }

    public Token peek(int i) {
        return tokens.get(i);
    }

    public Token poll() {
        return tokens.remove(0);
    }
}
